package com.github.shiqiyue.flow.control.autoconfigure;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.github.shiqiyue.flow.contol.FlowControlConfigurer;
import com.github.shiqiyue.flow.contol.dao.FlowControlDao;
import com.github.shiqiyue.flow.contol.dao.InMemoryFlowControlDao;
import com.github.shiqiyue.flow.contol.fliter.FlowControlFilter;
import com.github.shiqiyue.flow.contol.intercept.action.DefaultFlowControlInterceptAction;
import com.github.shiqiyue.flow.contol.intercept.action.FlowControlInterceptAction;
import com.github.shiqiyue.flow.contol.redis.name.strategy.DefaultRedisKeyNameStrategy;
import com.github.shiqiyue.flow.contol.redis.name.strategy.RedisKeyNameStrategy;

@Configuration
@ConditionalOnClass(FlowControlFilter.class)
@EnableConfigurationProperties(FlowControlProperties.class)
@ConditionalOnProperty(prefix = "flow.control", value = "enabled", havingValue = "true")
public class FlowControlConfigure {
	
	@Autowired
	private FlowControlProperties properties;
	
	@Bean(destroyMethod = "shutdown")
	@ConditionalOnMissingBean(value = RedissonClient.class)
	RedissonClient redissonClient() {
		Config config = new Config();
		config.useSingleServer().setAddress(properties.getRedisUrl());
		RedissonClient redisson = Redisson.create(config);
		return redisson;
	}
	
	@Bean
	@ConditionalOnMissingBean(value = RedisKeyNameStrategy.class)
	public RedisKeyNameStrategy redisKeyNameStrategy() {
		return new DefaultRedisKeyNameStrategy();
	}
	
	@Bean
	@ConditionalOnMissingBean(value = FlowControlInterceptAction.class)
	public FlowControlInterceptAction flowControlInterceptAction() {
		DefaultFlowControlInterceptAction flowControlInterceptAction = new DefaultFlowControlInterceptAction();
		flowControlInterceptAction.setJsonResponse(properties.getJsonResponse());
		return flowControlInterceptAction;
	}
	
	@Bean
	@ConditionalOnMissingBean(value = FlowControlDao.class)
	public FlowControlDao flowControlDao() {
		InMemoryFlowControlDao flowControlDao = new InMemoryFlowControlDao();
		flowControlDao.insertAll(properties.getItems());
		return flowControlDao;
	}
	
	@Bean
	@ConditionalOnMissingBean(value = FlowControlConfigurer.class)
	FlowControlConfigurer flowControlConfigurer(RedissonClient redissonClient,
			RedisKeyNameStrategy redisKeyNameStrategy, FlowControlInterceptAction flowControlInterceptAction,
			FlowControlDao flowControlDao) {
		FlowControlConfigurer flowControlConfigurer = new FlowControlConfigurer() {
			
			@Override
			public RedissonClient redissonClient() {
				return redissonClient;
			}
			
			@Override
			public RedisKeyNameStrategy redisKeyNameStrategy() {
				return redisKeyNameStrategy;
			}
			
			@Override
			public FlowControlInterceptAction flowControlInterceptAction() {
				return flowControlInterceptAction;
			}
			
			@Override
			public FlowControlDao flowControlDao() {
				return flowControlDao;
			}
		};
		return flowControlConfigurer;
	}
	
	@Bean
	@ConditionalOnMissingBean(value = FlowControlFilter.class)
	FilterRegistrationBean flowControlFilter(FlowControlConfigurer flowControlConfigurer) {
		FilterRegistrationBean registration = new FilterRegistrationBean();
		registration.setFilter(new FlowControlFilter(flowControlConfigurer));
		registration.addUrlPatterns(properties.getFilterUrlPatterns());
		registration.setName("flowControlFilter");
		registration.setOrder(1);
		return registration;
	}
	
}
